package dm.ast.model.gen;


import dm.ast.*;
import dm.ast.model.gen.factory.BaseModelFactory;
import dm.ast.model.gen.factory.CaseExpModelFactory;
import dm.ast.model.gen.factory.ComplexityModel;
import dm.ast.model.gen.factory.ExpressionModelFactory;
import dm.ast.pro.DmCastFun;
import test.utility.Randomly;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 根据模板AST生成表达式
 */
public class ExpressionGen extends ASTGenBase{
    public DmASTNode gen(int astLevel , int wid_t , int dep_t , DmDataType rtype){
        if(wid_t < 0){
            System.out.println(String.format("w:%d,d:%d" , wid_t , dep_t));
            throw new AssertionError();
        }
        ComplexityModel model = ExpressionModelFactory.expModels(wid_t , dep_t);
        DmExpression exp = new DmExpression();
        exp.setTargetWidt(wid_t);
        exp.setTargetDept(dep_t);
        exp.setASTLevel(astLevel);
        exp.reType = rtype;
        exp.setLeftExp(singleGen(astLevel , model.comps[0].wid_t , model.comps[0].dep_t , rtype));

        int i = 1;
        int tmp_wid_use = 0;
        int tmp_wid = 0;
        int tmp_dep = 0;
        while (i < model.count){
            DmOperator op = OperatorGen.expOperatorRandom(rtype);
            tmp_wid = model.comps[i].wid_t;
            if(tmp_wid == 0){   //中间宽度为0 ， 那么后续的宽度也为0 ，不必考虑
                break;
            }
            // 如果多次添加了一元操作符
            // 那么适当减少该部分的宽度
            tmp_wid = tmp_wid - tmp_wid_use;
            tmp_dep = model.comps[i].dep_t;
            if(op.getExpNum() == 1){
                tmp_wid_use++;
                // 如果该部分的宽度大于0 ， 那么加上此一元操作符
                // 否则，舍弃该一元操作符，同时，如果该部分的dep_t =1 ,那么舍弃model的这一部分
                if(tmp_wid > 0){
                    exp = buildExp(exp , op);
                } else {
                    if(tmp_dep == 1){ // 目标宽度小于3 ，且dep_t = 1 , 那么舍弃model的这一部分
                        i++;
                    }
                }

            }else {
                tmp_wid_use = 0;
                DmDataType rightType = getRightDataType(exp , op);
                exp = buildExp(exp , singleGen(astLevel , tmp_wid , tmp_dep , rightType) , op);
                i++;
            }
        }
        if(exp.targetWidt() > 2 && exp.getLeftExp() == null){
            throw new AssertionError();
        }
        return exp;
    }

    //生成指定类型的基础表达式
    public static DmExpression finishGen(DmDataType rtype , DmSingleExpType expType){
        DmExpression exp;
        switch (expType){
            case DM_COLUMN:
                exp = new DmColumn();
                break;
            case DM_CONSTANT:
                exp = DmConstantGen.gen(rtype);
                break;
            case DM_IFUN:
                exp = new DmIFun();
                break;
            default:
                throw new AssertionError(expType);
        }
        exp.reType = rtype;
        return exp;
    }

    //单一表达式生成
    //基本表达式类型
    //需要指定表达式类型
    public DmExpression singleGen(int astLevel , int wid_t , int dep_t , DmDataType rtype){
        int tar_wid = wid_t;
        int tar_dep = dep_t;

        DmExpression exp = null;
        DmExpression lExp = null;

        exp = new DmExpression();
        exp.setTargetWidt(wid_t);
        exp.setTargetDept(dep_t);
        exp.setASTLevel(astLevel);

        List<DmSingleExpType> validOptions = new ArrayList<>(Arrays.asList(DmSingleExpType.values()));
        if(tar_dep == 1 || wid_t <= 1){  //当深度或宽度为1的时候，不需要子查询
            validOptions.remove(DmSingleExpType.DM_SUBSELECT);
        }

        if(!testFlags[BaseModelFactory.INDEX_CASE] || wid_t <= BaseModelFactory.estimateMinCompWidt(dep_t)){
            validOptions.remove(DmSingleExpType.DM_CASE);

        }

        validOptions.remove(DmSingleExpType.DM_CAST);
        if( wid_t > 3 && wid_t <10&&Randomly.getBooleanWithSmallProbability() ){
            validOptions.add(DmSingleExpType.DM_CAST);
        }
        validOptions.remove(DmSingleExpType.DM_IFUN);
        DmSingleExpType expType = Randomly.fromList(validOptions);


//        if(BaseModelFactory.ifunFlag && Randomly.getBooleanWithSmallProbability()){
//            validOptions.add(DmSingleExpType.DM_IFUN);
//        }

        switch (expType){
            case DM_COLUMN:  //列
            case DM_CONSTANT: //常量
//            case DM_IFUN:
                lExp = finishGen(rtype , expType);
                if(tar_wid > 2 ){  // 宽度大于2 ，考虑多个表达式构成一个表达式
                    DmOperator op = OperatorGen.expOperatorRandom(rtype);
                    if(op.getExpNum() != 1){  //通过2个表达式和二元操作符构造表达式 //可构造复合表达式
                        DmDataType rightType = getRightDataType(lExp , op);
                        exp.setLeftExp(buildExp(lExp , singleGen(astLevel , tar_wid - 2 , tar_dep , rightType) , op));
                    }else {  //通过1个表达式和一元操作符构造表达式 //可构造复合表达式
                        exp.setLeftExp(buildExp(singleGen(astLevel , tar_wid - 1 , tar_dep , rtype) , op));

                    }
                }else{
                    exp.setLeftExp(lExp);
                }
                break;
            case DM_SUBSELECT: //子查询
                exp.setLeftExp(subSelectGen(astLevel + 1 , tar_wid , tar_dep , rtype));
                break;
            case DM_CASE: //情况表达式
                exp = DmCaseGen(astLevel , tar_wid - 1 , tar_dep , rtype);
                break;
            case DM_CAST:
                exp = DmCastGen(astLevel , tar_wid - 2 , tar_dep , rtype);
                break;
            default:
                throw new AssertionError(expType);

        }
        exp.reType = rtype;
        exp.setASTLevel(astLevel);
        return exp;

    }

    public DmASTNode gen(int astLevel , int wid_t , int dep_t){
        DmDataType rtype = Randomly.fromList(supportedExpRetType());
        return gen(astLevel , wid_t , dep_t , rtype);
    }

    //生成cast表达式
    private DmCastFun DmCastGen(int astLevel , int tar_wid , int tar_dep , DmDataType rtype) {
        DmCastFun dmCastExp = new DmCastFun();
        dmCastExp.setTargetWidt(tar_wid);
        dmCastExp.setTargetDept(tar_dep);
        dmCastExp.setASTLevel(astLevel);

        List<DmDataType> typeList = new ArrayList<>();
        typeList.add(DmDataType.INT);
        typeList.add(DmDataType.CHAR);
        rtype = Randomly.fromList(typeList);

        DmExpression castExpression = singleGen(astLevel , tar_wid , tar_dep , rtype);
        dmCastExp.setCastExp(castExpression);
        return dmCastExp;
    }

    private DmCase DmCaseGen(int astLevel , int tar_wid , int tar_dep , DmDataType rtype){
        DmCase dmCase = new DmCase();
        dmCase.setTargetWidt(tar_wid);
        dmCase.setTargetDept(tar_dep);
        dmCase.setASTLevel(astLevel);

        if(Randomly.getBoolean()){  //简单情况
            // 知道分成三个模板，一个给valueExpression , 两个给when Then ， 剩余的成对给whenThen ,单的给elseValue
            ComplexityModel caseExpModel = CaseExpModelFactory.caseExpModels(tar_wid , tar_dep , 3);
            dmCase.setValueExpression(
                    (DmExpression)gen(astLevel , caseExpModel.comps[0].wid_t , caseExpModel.comps[0].dep_t , rtype));
            int index = 1;
            if(caseExpModel.count % 2 == 0){
                dmCase.setElseValue(
                        (DmExpression)gen(astLevel , caseExpModel.comps[index].wid_t, caseExpModel.comps[index].dep_t , rtype)
                );
                index++;
            }
            while (index < caseExpModel.count){
                dmCase.addWhenThen(
                        (DmExpression)gen(astLevel , caseExpModel.comps[index].wid_t , caseExpModel.comps[index].dep_t , rtype) ,
                        (DmExpression)gen(astLevel , caseExpModel.comps[index + 1 ].wid_t , caseExpModel.comps[index + 1].dep_t , rtype));
                index += 2;
            }
        } else { // 搜索情况
            int index = 0;
            // 至少分成两个模板， 给WhenThen , 剩余的成对给whenThen , 单的给elseValue
            ComplexityModel caseExpModel = CaseExpModelFactory.caseExpModels(tar_wid , tar_dep , 2);
            if(caseExpModel.count % 2 == 1){
                dmCase.setElseValue(
                        (DmExpression)gen(astLevel , caseExpModel.comps[index].wid_t, caseExpModel.comps[index].dep_t , rtype)
                );
                index++;
            }
            while (index < caseExpModel.count){
                BoolExpressionGen boolExpressionGen = new BoolExpressionGen(testFlags);
                DmBoolExpression when = (DmBoolExpression) boolExpressionGen.gen(astLevel , caseExpModel.comps[index].wid_t ,
                        caseExpModel.comps[index].dep_t);
                dmCase.addWhenThen(when , (DmExpression)gen(astLevel , caseExpModel.comps[index + 1 ].wid_t , caseExpModel.comps[index + 1].dep_t , rtype));
                index += 2;
            }
        }
        return dmCase;
    }

    public DmExpression subSelectGen(int astLevel , int wid_t ,int dep_t , DmDataType rtype){
        SelectGen selGen = new SelectGen();
        DmSelect select = (DmSelect)selGen.gen(astLevel , wid_t , dep_t - 1 , 1);
        //TODO
        //需要设置查询的列类型
        //需要考虑子查询的模型
        DmExpression exp = new DmExpression();
        DmSubSelect subSel = new DmSubSelect(select);
        exp.setLeftExp(subSel);
        exp.setTargetWidt(select.targetWidt());
        exp.setTargetDept(select.targetDept());
        exp.setASTLevel(astLevel);
        exp.reType = rtype;
        return exp;
    }

    //TODO
    //需要修改
    //目前仅是简单示例
    public DmASTNode genSelElements(int astLevel , int wid_t , int dep_t){
        // TODO Auto-generated method stub
        DmDataType rType = Randomly.fromList(supportedExpRetType());
        return singleGen(astLevel , wid_t , dep_t , rType);
    }

    //update
    public DmASTNode genSelElements(int astLevel , int wid_t , int dep_t,DmDataType rType){
        return singleGen(astLevel , wid_t , dep_t , rType);
    }

    //生成一个随机类型的列
    public static DmColumn genRandColumn(){
        DmDataType rType = Randomly.fromList(supportedExpRetType());
        DmColumn col = new DmColumn();
        col.reType = rType;
        return col;
    }

    //根据左表达式、操作符，确定右表达式类型
    //目前仅单独处理时间类型不能运算的问题
    //后续还要考虑类型兼容性问题
    public static DmDataType getRightDataType(DmExpression lexp , DmOperator op){
        if(lexp.reType != DmDataType.TIME){
            return lexp.reType;
        } else {
            return DmDataType.INTERVAL;
        }
    }

    //左操作数，右操作数，操作符确定新表达式的返回类型
    //目前仅单独处理时间类型和间隔类型运算的问题
    //后续还要考虑类型兼容性问题
    public static DmDataType getBuildExpReType(DmExpression lexp , DmExpression rexp , DmOperator op){
        if(lexp.reType == DmDataType.TIME || rexp.reType == DmDataType.TIME){
            return DmDataType.TIME;
        }
        return lexp.reType;
    }

    //返回当前支持的表达式返回类型集合
    public static List<DmDataType> supportedExpRetType(){
        List<DmDataType> allReturnTypes = new ArrayList<>(Arrays.asList(DmDataType.values()));

        allReturnTypes.remove(DmDataType.BOOLEAN); //属于单独的bool表达式
        //以下类型还未支持复合表达式
        allReturnTypes.remove(DmDataType.BINARY);
        allReturnTypes.remove(DmDataType.TIME);
        allReturnTypes.remove(DmDataType.INTERVAL);
        allReturnTypes.remove(DmDataType.CLOB);
        allReturnTypes.remove(DmDataType.BLOB);
        return allReturnTypes;
    }

    //通过1个表达式和一元操作符构造表达式
    //可构造复合表达式
    public static DmExpression buildExp(DmExpression exp , DmOperator op){
        DmExpression expression = new DmExpression();
        expression.setTargetDept(exp.targetDept());
        expression.setTargetWidt(exp.targetWidt() + 1);
        expression.setASTLevel(exp.getSelLevel());
        expression.setLeftExp(exp);
        expression.setOperator(op);
        expression.reType = exp.reType;
        return expression;
    }

    //通过2个表达式和二元操作符构造表达式
    //可构造复合表达式
    public static DmExpression buildExp(DmExpression lexp , DmExpression rexp , DmOperator op){
        DmExpression expression = new DmExpression();
        expression.setTargetDept(lexp.targetDept() > rexp.targetDept() ? lexp.targetDept() : rexp.targetDept());
        expression.setTargetWidt(lexp.targetWidt() + 1 + rexp.targetWidt());
        expression.setASTLevel(lexp.getSelLevel());
        expression.setLeftExp(lexp);
        expression.setOperator(op);
        expression.setRightExp(rexp);
        expression.reType = ExpressionGen.getBuildExpReType(lexp , rexp , op);
        return expression;
    }

}

